///\todo upgrade

//////////////////////////////////////////////////////////////////////////////////
//                                                                              //
//  This file is part of the buola project (https://code.google.com/p/buola/).  //
//                                                                              //
//  Copyright(c) 2007-2012 Xavi Gratal                                          //
//  gratal AT gmail DOT com                                                     //
//                                                                              //
//  Buola is free software: you can redistribute it and/or modify               //
//  it under the terms of the GNU General Public License as published by        //
//  the Free Software Foundation, either version 3 of the License, or           //
//  (at your option) any later version.                                         //
//                                                                              //
//  Buola is distributed in the hope that it will be useful,                    //
//  but WITHOUT ANY WARRANTY; without even the implied warranty of              //
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               //
//  GNU General Public License for more details.                                //
//                                                                              //
//  You should have received a copy of the GNU General Public License           //
//  along with buola.  If not, see <http://www.gnu.org/licenses/>.              //
//                                                                              //
//////////////////////////////////////////////////////////////////////////////////

#ifndef _BUOLA_DBUS_CPROXY_H_
#define _BUOLA_DBUS_CPROXY_H_

#include <buola/dbus/cbus.h>
#include <buola/dbus/cmessage.h>
#include <buola/utility/usignal.h>

namespace buola { namespace dbus {

///\addtogroup dbus
///@{

/////////////////////////////////////////////////////////////////////////

/// DBUS object proxy

/// represents a remote DBus object. It can be used to invoke methods
/// and receive signals from the object
///
/////////////////////////////////////////////////////////////////////////

class CProxy
{
public:
//! \name constructor and destructor
//! can only be called from friend classes (CBus)
//@{
    CProxy(CBus *pBus,const std::string &pService,const std::string &pPath)
        :   mBus(pBus)
        ,   mService(pService)
        ,   mPath(pPath)
    {}
    
    CProxy(const CProxy &pRH)
        :   mBus(pRH.mBus)
        ,   mService(pRH.mService)
        ,   mPath(pRH.mPath)
    {}
    
    CProxy(CProxy &&pRH)
        :   mBus(pRH.mBus)
        ,   mService(std::move(pRH.mService))
        ,   mPath(std::move(pRH.mPath))
    {}
    
private:
    //used only from CMessage
    CProxy()
        :   mBus(nullptr)
    {}
//@}

public:
//! \name public interface
//@{
    //! call method \p pMethod, wait for reply and return it
    template<typename... tParams>
    PMessage Call(const std::string &pInterface,const std::string &pMethod,const tParams&... pParams) const
    {
        CMessage lMsg(nMethodCall,*this,pInterface,pMethod);
        lMsg.AppendVariadic(pParams...);
        return mBus->SendMessageWait(&lMsg);
    }

    //! call method \p pMethod, and return without waiting for a reply
    template<typename... tParams>
    void CallIgnore(const std::string &pInterface,const std::string &pMethod,const tParams&... pParams) const
    {
        CMessage lMsg(nMethodCall,*this,pInterface,pMethod);
        lMsg.AppendVariadic(pParams...);
        mBus->SendMessage(&lMsg);
    }

    //! call method \p pMethod, and return a future
    template<typename... tParams>
    PMessage CallAsync(const std::string &pInterface,const std::string &pMethod,const tParams&... pParams) const
    {
        CMessage lMsg(nMethodCall,*this,pInterface,pMethod);
        lMsg.AppendVariadic(pParams...);
        return mBus->SendMessageEnqueue(&lMsg);
    }

    template<typename tReturn,typename... tParams>
    tReturn SimpleCall(const std::string &pInterface,const std::string &pMethod,const tParams&... pParams) const
    {
        CMessage lMsg(nMethodCall,*this,pInterface,pMethod);
        lMsg.AppendVariadic(pParams...);
        PMessage lReply=mBus->SendMessageWait(&lMsg);
        tReturn lRet;
        lReply->Extract(lRet);
        return lRet;
    }

    template<typename tType>
    tType GetProperty(const std::string &pInterface,const std::string &pName) const
    {
        PMessage lReply=Call("org.freedesktop.DBus.Properties","Get",pInterface,pName);
        tType lRet;
        lReply->ExtractVariant(lRet);
        return lRet;
    }

    //! connect to a signal on the remote object
    template<typename... tArgs>
    void Connect(const std::string &pInterface,tArgs&&... pArgs) const
    {
        CBus::tSignal *lSignal=mBus->RegisterInterfaceMatch(mPath,pInterface);
        lSignal->Connect(std::forward<tArgs>(pArgs)...);
    }
    
    //! connect to all signals on the remote object
    template<typename... tArgs>
    void ConnectAll(tArgs&&... pArgs) const
    {
        CBus::tSignal *lSignal=mBus->RegisterObjectMatch(mPath);
        lSignal->Connect(std::forward<tArgs>(pArgs)...);
    }
    
    //! copy assignment operator
    CProxy &operator=(const CProxy &pRH)
    {
        mBus=pRH.mBus;
        mService=pRH.mService;
        mPath=pRH.mPath;
        
        return *this;
    }

    //! move assignment operator
    CProxy &operator=(CProxy &&pRH)
    {
        mBus=pRH.mBus;
        mService=std::move(pRH.mService);
        mPath=std::move(pRH.mPath);
        
        return *this;
    }

    CBus *GetBus() const   {   return mBus;    }
    const std::string &GetService() const     {   return mService;    }
    const std::string &GetPath() const    {   return mPath;    }
//@}

private:
    CBus *mBus;
    std::string mService;
    std::string mPath;
    
    friend class CMessage;
};

///@}

/*namespace dbus*/ } /*namespace buola*/ }

#endif
